package fybug.nulll.pdstream.WR.Stream;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;

import fybug.nulll.pdfunctionlibrary.Annotations.MaybeStop;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.Source.StreamSource;
import fybug.nulll.pdstream.StreamOperator;
import fybug.nulll.pdstream.WR.Err.CharStreamErr;
import fybug.nulll.pdstream.WR.Stream.Interface.ReadCheck;
import fybug.nulll.pdstream.WR.Stream.Interface.StringCheck;
import fybug.nulll.pdstream.WR.W_R;

import static fybug.nulll.pdstream.WR.Stream.Help.getLine;
import static fybug.nulll.pdstream.WR.Stream.Help.upBufferSize;
/**
 * <h2>字符流读取器.</h2>
 * <pre>
 * <b>建议使用工场类 {@link W_R} 构造</b>
 *
 * 从绑定的 <b>操作源</b> 中获取的被操作流进行读取
 * 支持 <b>批量读取</b>
 * 支持 <b>按行读取</b> ，会标记当前流
 * 可使用该操作器中的方法对操作源进行 <b>流的重绑定</b>
 * 由于 <b>字符流的读取方式</b> 所有操作均为 <b>连续操作</b> ,可有效解决不稳定流的读取问题
 *
 * 但不建议使用该类中的方法 <b>读取大量数据</b>
 * 会 <b>内存不足</b> 的
 * <b>亦不要将片段大小设置的太大</b>
 * </pre>
 * <pre>常规操作方法：
 * {@link #read()}
 * </pre>
 *
 * @author fybug
 * @version 0.2.0
 * @see StreamOperator
 * @see Reader
 * @see W_R
 * @see CharStreamErr
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public final
class Read extends StreamOperator<Reader, Object> {
    // 当前操作流组中的流
    private int streamMark = 0;

    /**
     * <p>构造一个读取器并绑定文本源.</p>
     *
     * @param r 被读取器作为读取源的操作源
     */
    public
    Read(@Nullable final StreamSource<Reader, Object> r) { binSource(r); }

    /*
     * MainSource
     */

    /**
     * <p>绑定要读取的文本源.</p>
     *
     * @param r 被读取器作为数据来源的操作源
     *
     * @return this
     */
    @NotNull
    public final
    Read binSource(@Nullable final StreamSource<Reader, Object> r)
    {
        synchronized ( this ){
            streamMark = 0;
            super.binSource(r);
        }
        return this;
    }

    /**
     * <p>重绑定操作源中的流.</p>
     *
     * @param readers 要绑定到被绑定的源中的流
     *
     * @return this
     */
    @NotNull
    public final
    Read binReader(@Nullable final Reader... readers) {
        try {
            synchronized ( this ){
                streamMark = 0;
                source().binStream(readers);
            }
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /**
     * <p>增加操作源中的流.</p>
     *
     * @param readers 要追加到被绑定的源中的流
     *
     * @return this
     */
    @NotNull
    public final
    Read appendReader(final Reader... readers) {
        try {
            @NotNull StreamSource<Reader, Object> source = source();
            Reader[] re;
            try {
                re = source.stream();
            } catch ( Stop e ) {
                re = null;
            }
            binReader(Arrarys.append(re, readers));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /*
     * Read
     */

    /**
     * <p>读取绑定的流的字符串.</p>
     * <pre>
     * 从 <b>当前标记</b>  的流开始将流中的字符逐个读出,并缓存在 {@link StringBuilder}
     * 直到读取到最后一个流的流尾
     * 适用于所有流
     * </pre>
     *
     * @return 读取出来的字符串 ( 如果读取不到会返回一个空的字符串 )
     *
     * @throws CharStreamErr 读取过程发生错误
     */
    @NotNull
    public final
    String read() throws CharStreamErr {
        @NotNull Reader[] r;
        @NotNull //* 使用字符缓冲区,降低修改开销 *//
                StringBuilder stringBuffer = new StringBuilder(); // 读取缓存
        @NotNull Reader re = null; // 减少寻址次数
        int i; // 单次读取返回
        try {
            synchronized ( this ){
                r = source().stream(); // 读取流组
                for ( int i1 = streamMark; i1 < r.length; i1++ ){
                    /* 读取所有流 */
                    streamMark = i1;
                    re = r[i1];
                    while( (i = re.read()) != -1 )
                        // 读取一个字符并放入缓存区
                        stringBuffer.append((char) i);
                }
            }
            i = 0;
            re = null;
        } catch ( IOException e ) {
            throw new CharStreamErr(e.getMessage(), re, null);
        } catch ( Stop ignored ) {
            return "";
        }
        return stringBuffer.toString();
    }

    /**
     * <p>读取绑定的流中的一行字符串.</p>
     * <pre>
     * 从 <b>当前标记</b> 的流开始将流中的字符逐个读出,并缓存在 {@link StringBuilder}
     * 直到读取到{@code "\n"} 或当前流的末尾，如果当前流 <b>已没有数据</b> ，会读取下一个流
     * 适用于所有流
     * </pre>
     *
     * @return 读取的一行字符串，如果未读取到任何东西则会返回{@code null}
     *
     * @throws CharStreamErr 读取过程发生错误
     */
    @Nullable
    public final
    String readLine() throws CharStreamErr {
        @NotNull Reader[] r;
        @NotNull //* 使用字符缓冲区,降低修改开销 *//
                StringBuilder stringBuffer = new StringBuilder(); // 读取缓存
        int ma; // 读取状态标识
        try {
            synchronized ( this ){
                r = source().stream(); // 读取流组
                int[] is = getLine(stringBuffer, streamMark, r);
                streamMark = is[0];
                ma = is[1];
            }
        } catch ( Stop ignored ) {
            return null;
        }
        if (stringBuffer.length() == 0 && ma == 0)
            // 是否未读取到任何东西
            return null;
        return stringBuffer.toString();
    }

    /**
     * <p>使用连续操作从绑定的流中进行读取.</p>
     * <pre>
     * 从 <b>当前标记</b> 的流开始将绑定的流中的字符读取
     * 每次读取一个片段,直到读取到 <b>最后一个流的流尾</b>
     * 可使用 <b>中介接口</b> 对读取的片段进行处理
     * 适用于所有流
     * </pre>
     *
     * @param size  片段大小
     * @param check 中介接口({@code null} 为不处理)
     *
     * @return 返回被接口处理好的字符串 ( 读取不到会返回空字符串 )
     *
     * @throws CharStreamErr 读取出错
     * @see StringCheck
     */
    @NotNull
    public final
    String sustained(final int size, @Nullable final StringCheck check) throws CharStreamErr
    {
        return monitor(size, check == null ? null : new ReadCheck() {
            @NotNull
            @Override
            @MaybeStop
            public
            String check(@Nullable final String s) throws Stop { return check.check(s); }

            @Override
            @MaybeStop
            public
            void init() { check.init(); }

            @Override
            public
            void destroy() { check.destroy(); }
        });
    }

    /**
     * <p>使用连续操作从绑定的流中进行读取.</p>
     * <pre>
     * 从 <b>当前标记</b> 的流开始将绑定的流中的字符读取
     * 每次读取一个片段,直到读取到 <b>最后一个流的流尾</b>
     * 可以使用 <b>监听接口</b> <b>处理读取的数据并动态调整读取片段</b> 大小
     * 在读取过程中设置片段大小为{@code 0} 可以中断读取
     * 适用于所有流
     * </pre>
     *
     * @param size 初始片段大小
     * @param see  监听接口 ({@code null} 为不监听)
     *
     * @return 返回被接口处理好的字符串 ( 读取不到会返回空字符串 )
     *
     * @throws CharStreamErr 读取过程发生错误
     * @see ReadCheck
     */
    @NotNull
    public final
    String monitor(final int size, @Nullable final ReadCheck see) throws CharStreamErr {
        @NotNull Reader[] r;
        @NotNull //* 使用缓冲输出流 *//
                CharArrayWriter writer = new CharArrayWriter(); // 暂存区
        @NotNull //* 使用 Nio 包的缓冲区,提高缓冲区复用效率 *//
                CharBuffer charBuffer = CharBuffer.allocate(size); // 缓冲区
        @NotNull String bufferText;
        @Nullable Reader re = null; // 减少寻址
        int readSize; // 读取数量
        try {
            if (see != null)
                see.init();
            synchronized ( this ){
                r = source().stream(); // 读取流组
                if (size <= 0)
                    return "";
                for ( int i = streamMark; i < r.length; i++ ){
                    /* 读取所有流 */
                    re = r[i];
                    streamMark = i;
                    while( (readSize = re.read(charBuffer)) != -1 ){
                        /* 读取到缓冲区并检查数量 */
                        bufferText = String.valueOf(charBuffer.array(), 0, readSize);
                        // 使用检查接口
                        bufferText = see != null ? see.check(bufferText) : bufferText;
                        writer.write(bufferText == null ? "" : bufferText);
                        bufferText = null;
                        if (readSize < charBuffer.capacity())
                            /* 读取完成 */
                            break;
                        charBuffer = upBufferSize(see, charBuffer, charBuffer.capacity());
                        bufferText = null;
                    }
                }
            }
            readSize = 1;
            charBuffer = null;
            re = null;
        } catch ( Stop ignored ) {
            return "";
        } catch ( IOException e ) {
            // 读取出错
            throw new CharStreamErr(e.getMessage(), re, null);
        } finally {
            if (see != null)
                see.destroy();
        }
        return writer.toString();
    }
}